package com;

import com.spring.mvc.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;

/**
 * Created by Admin on 9/21 0021.
 */

//@SessionAttributes(value = {"user","user1"},types = {String.class})
@RequestMapping("/helloRequestMapping")
@Controller
public class HelloRequestMapping {
    private static final String SUCCESS="success";
    @RequestMapping("/testRequestMapping")
    public String testRequestMapping(){
        System.out.println("RequestMapping");
        return SUCCESS;
    }

    /**
     * value : 指定url ;
     * method :指定请求方式
     * @return
     */
    @RequestMapping( value = "/testMethod",method = RequestMethod.POST)
    public String testMethod(){
        System.out.println("testMethod");
        return SUCCESS;
    }

    @RequestMapping(value = "/testParamsAndHeader",params = {"username","age!=10"})
    public String testParamsAndHeader(){
        System.out.println("testParamsAndHeader");
        return SUCCESS;
    }
    @RequestMapping("/testPathVariable/{id}")
    public String testPathVariable(@PathVariable("id") Integer id){
        System.out.println("testPathVariable:"+id);
        return SUCCESS;
    }

    /**
     * Rest 风格的 URL
     * 新增: /order  POST
     * 修改: /order/1 PUT
     * 获取: /order/1 GET
     * 删除: /order/1 DELETE
     *
     * 如何发送URL ?
     * 1.需要配置 HiddenHttpMethodFilter
     * 2.需要发送POST 请求
     * 3.需要在发送POST 请求时添加隐藏域 : name="_method" value=PUT/DELETE
     *
     * 在SpringMvc 的目标方法中如何获得id ;
     * 添加注解: @PathVariable
     */
    @RequestMapping(value = "/testRest/{id}",method = RequestMethod.GET)
    public String testGet(@PathVariable Integer id){
        System.out.println("testGet: "+ id);
        return SUCCESS;
    }

    @RequestMapping(value = "/testRest",method = RequestMethod.POST)
    public String testPost(){
        System.out.println("testPost");
        return SUCCESS;
    }

    @RequestMapping(value = "/testRest/{id}",method = RequestMethod.DELETE)
    public String testDelete(@PathVariable Integer id){
        System.out.println("testDelete:" + id);
        return SUCCESS;
    }

    @RequestMapping(value = "/testRest/{id}",method = RequestMethod.HEAD)
    public String testPut(@PathVariable Integer id){
        System.out.println("testPut:" + id);
        return SUCCESS;
    }

    /**
     * RequestParam: 来映射请求参数
     * value : 请求参数的名字
     * required: 该参数是否必须,默认为true;
     * defaultValue: 请求参数的默认值
     * @param un
     * @param age
     * @return
     */
    @RequestMapping("/testRequestParam")
    public String testRequestParam(@RequestParam(value = "username") String un,
                                   @RequestParam(value = "age",required = false,defaultValue = "0") int age){
        System.out.println("testRequestParam: "+un+", "+age);
        return SUCCESS;
    }

    @RequestMapping("/testRequest")
    public String testRequestHeader(@RequestHeader(value = "Accept-Language") String str){
        System.out.println("testRequest: Accept-Language:"+str);
        return SUCCESS;
    }

    @RequestMapping("/testPojo")
    public String testPojo(User user){
        System.out.println("testPojo: "+user);
        return SUCCESS;
    }

    /**
     * 可以使用Servlet 原声的 API 作为目标方法的参数, 具体支持一下类型
     * HttpServletRequest
     * HttpServletResponse
     * HttpSession
     * Writer
     * Reader
     * OutPutStream
     * Locale InPutStream
     *
     * .....
     * @param request
     * @param response
     * @param out
     * @throws IOException
     */
    @RequestMapping("/testHttpServletAPI")
    public void testHttpServletAPI(HttpServletRequest request,
                                     HttpServletResponse response, Writer out) throws IOException {
        System.out.println("request: "+request+",response: " +response);
        out.write("hello Servlet");
       // return SUCCESS;
    }

    /**
     * 目标方法的返回值可以是ModelAndView  类型
     * 其中可以包含视图信息和数据模型
     * SpringMvc 会把 ModelAndView 的Model 中的数据放入到Request 中
     * 这样在JSP 页面就能通过 RequestScope 获取信息;
     * @return
     */
    @RequestMapping("/testModelAndView")
    public ModelAndView testModelAndView(){
        String view=SUCCESS;
        ModelAndView modelAndView=new ModelAndView(view);
        //添加视图模型到 ModelAndView 中
        modelAndView.addObject("time",new Date());
        return modelAndView;
    }

    @RequestMapping("/testMap")
    public ModelAndView testMap(){
        String view=SUCCESS;
        ModelAndView modelAndView=new ModelAndView(view);
        modelAndView.addObject("name", Arrays.asList("Tom","Jerry","Rose"));
        return modelAndView;
    }

//    @RequestMapping("/testMap")
//    public String testMap(Map<String,Object>map){
//        map.put("name", Arrays.asList("Tom","Jerry","Rose"));
//        return SUCCESS;
//    }
    @RequestMapping("/testSessionAttributes")
    public String testSessionAttributes(Map<String,Object> map){
        map.put("user",new User("Tom","12345",12));
        map.put("user1",new User("Jerry","12345",12));
        map.put("school","bai");
        return SUCCESS;
    }

    /**
     *运行流程:
     * 1.执行 ModelAttribute 注解修饰的方法,从数据库中获取对象,然后将对象放入到Map 中,键为: user
     * 2.SpringMVC  从Map 中取出对象,然后将表单中的参数赋给 USer对象对应的属性,已经进行修改(不可见SpringMVC)
     * 3.SpringMVC 把上述对象传入目标方法的参数;
     *
     *
     * 注意: 在 @ModelAttribute 修饰的方法中, 放入到 Map 时的键需要和目标方法入参类型的第一个字母小写的字符串一致!
     *
     * SpringMVC 确定目标方法 POJO 类型入参的过程
     * 1. 确定一个 key:
     * 1). 若目标方法的 POJO 类型的参数木有使用 @ModelAttribute 作为修饰, 则 key 为 POJO 类名第一个字母的小写
     * 2). 若使用了  @ModelAttribute 来修饰, 则 key 为 @ModelAttribute 注解的 value 属性值.
     * 2. 在 implicitModel 中查找 key 对应的对象, 若存在, 则作为入参传入
     * 1). 若在 @ModelAttribute 标记的方法中在 Map 中保存过, 且 key 和 1 确定的 key 一致, 则会获取到.
     * 3. 若 implicitModel 中不存在 key 对应的对象, 则检查当前的 Handler 是否使用 @SessionAttributes 注解修饰,
     * 若使用了该注解, 且 @SessionAttributes 注解的 value 属性值中包含了 key, 则会从 HttpSession 中来获取 key 所
     * 对应的 value 值, 若存在则直接传入到目标方法的入参中. 若不存在则将抛出异常.
     * 4. 若 Handler 没有标识 @SessionAttributes 注解或 @SessionAttributes 注解的 value 值中不包含 key, 则
     * 会通过反射来创建 POJO 类型的参数, 传入为目标方法的参数
     * 5. SpringMVC 会把 key 和 POJO 类型的对象保存到 implicitModel 中, 进而会保存到 request 中.
     *
     */
    @RequestMapping("/testModelAttribute")
    public String testModelAttribute(@ModelAttribute("abc") User user){
        System.out.println("修改: " + user);
        return SUCCESS;
    }
    /**
     * 有标记ModelAttribute 的方法,会在每个目标方法执行之前被调用,就是说会在目标方法之前执行,
     *2. @ModelAttribute 注解也可以来修饰目标方法 POJO 类型的入参, 其 value 属性值有如下的作用:
     * 1). SpringMVC 会使用 value 属性值在 implicitModel 中查找对应的对象, 若存在则会直接传入到目标方法的入参中.
     * 2). SpringMVC 会一 value 为 key, POJO 类型的对象为 value, 存入到 request 中.
     * @param id
     * @param map
     */
    @ModelAttribute
    public void getUser(@RequestParam(value = "id",required = false)Integer id,Map<String,Object> map){
        System.out.println("在任何目标方法之前执行 ModelAttribute 方法");
        if(id!=null){
            //模拟从数据库获取对象
            User user=new User(1,"Tom","12345",12);
            System.out.println("从数据库中获取user对象: "+user);
            map.put("abc",user);
        }
    }


    @RequestMapping("/testRequest")
    public String testRequest(){
        System.out.println("testRequest");
        return SUCCESS;
    }

    @RequestMapping("/testResponse")
    public String testResponse(){
        System.out.println("testResponse");
        return "redirect:/index.jsp";
    }

    @RequestMapping("/testView")
    public String testView(){
        return "helloView";
    }

}
